package com.wxb.springboot.springmvc.config;

import org.apache.http.HttpResponse;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

/**
 * RestTemplate use HttpComponentsClientHttpRequestFactory
 *
 * @author Feng Yu
 * @version V1.0
 * @date 2017-05-16
 */
@Configuration
public class RestTemplateConfiguration {

	private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfiguration.class);

	/**
	 * 连接池最大连接数
	 */
	@Value("${rest.http.client.MaxTotal:2000}")
	private int maxTotal;

	/**
	 * 连接池同路由并发数
	 */
	@Value("${rest.http.client.DefaultMaxPerRoute:500}")
	private int defaultMaxPerRoute;

	/**
	 * 连接超时
	 */
	@Value("${rest.http.client.ConnectTimeout:20000}")
	private int connectionTimeout;

	/**
	 * 数据读取超时时间
	 */
	@Value("${rest.http.client.ReadTimeout:20000}")
	private int readTimeout;

	/**
	 * 连接不够用的等待时间
	 */
	@Value("${rest.http.client.ConnectionRequestTimeout:5000}")
	private int connectionRequestTimeout;

	/**
	 * 若服务端没回timeout, 客户端默认长连接超时时间，单位秒
	 */
	@Value("${rest.http.client.DefaultKeepAliveTimeSecs:120}")
	private int defaultKeepAliveTimeSecs;

	/**
	 * 是否开启定时检测连接（空闲超时及过期）
	 */
	@Value("${rest.http.client.Evictable:false}")
	private boolean evictable;

	/**
	 * evictable=true时，检测时间间隔及空闲超时时长，单位秒
	 */
	@Value("${rest.http.client.EvictableIdleTimeSecs:120}")
	private int evictableIdleTimeSecs;

	/**
	 * 是否开启请求重试
	 */
	@Value("${rest.http.client.RetryEnable:true}")
	private boolean retryEnable;

	/**
	 * 请求重试次数
	 */
	@Value("${rest.http.client.RetryCount:2}")
	private int retryCount;

	@Bean
	@ConditionalOnMissingBean({HttpComponentsClientHttpRequestFactory.class})
	public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
		try {
			HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
			//是否开启定时检测
			if (evictable) {
				//关闭过期连接
				httpClientBuilder.evictExpiredConnections();
				//关闭空闲超时连接
				httpClientBuilder.evictIdleConnections(evictableIdleTimeSecs, TimeUnit.SECONDS);
			}
			//若服务端没有返回timeout，则给默认值，不再长期有效
			httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy() {
				@Override
				public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
					long keepAlive = super.getKeepAliveDuration(response, context);
					if (keepAlive == -1) {
						keepAlive = defaultKeepAliveTimeSecs * 1000;
					}
					return keepAlive;
				}
			});
			//SSL
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {

				@Override
				public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
					return true;
				}
			}).build();
			httpClientBuilder.setSSLContext(sslContext);
			//
			HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
			SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
			// 注册http和https
			Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
					.register("http", PlainConnectionSocketFactory.getSocketFactory())
					.register("https", sslConnectionSocketFactory).build();
			// 连接池
			PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
			// 最大连接数
			poolingHttpClientConnectionManager.setMaxTotal(maxTotal);
			// 同路由并发数
			poolingHttpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
			httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
			// 重试次数，默认3次未开启
			httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount, retryEnable));
			// httpClient连接配置
			CloseableHttpClient httpClient = httpClientBuilder.build();
			HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
			// 连接超时
			clientHttpRequestFactory.setConnectTimeout(connectionTimeout);
			// 数据读取超时时间
			clientHttpRequestFactory.setReadTimeout(readTimeout);
			// 连接不够用的等待时间，不宜过长，必须设置，比如连接不够用时，时间过长将是灾难性的
			clientHttpRequestFactory.setConnectionRequestTimeout(connectionRequestTimeout);
			// 缓冲请求数据，默认值是true。通过POST或者PUT大量发送数据时，建议将此属性更改为false，以免耗尽内存。
			// clientHttpRequestFactory.setBufferRequestBody(false);
			return clientHttpRequestFactory;
		} catch (Exception e) {
			LOGGER.error(e.getMessage());
		}
		return null;
	}

	@Bean
	@ConditionalOnMissingBean({RestTemplate.class})
	public RestTemplate createRestTemplate(HttpComponentsClientHttpRequestFactory clientHttpRequestFactory) {
		RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
		//修改RestTemplate默认converters替换StringHttpMessageConverter
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
		stringHttpMessageConverter.setWriteAcceptCharset(false);
		restTemplate.getMessageConverters().set(1, stringHttpMessageConverter);
		return restTemplate;
	}

}
